/*********************************************************************************
  *Copyright (c)    2021   xldeng
  *FileName:        config.h
  *Author:          xldeng
  *Version:         1.0
  *Date:            2021/2/26 12:36
  *Description:     配置模块
  *Others:
  *Function List:
     1.…………
  *History:
     1.Date:
       Author:
       Modification:
**********************************************************************************/

#ifndef SYLAR_CONFIG_H
#define SYLAR_CONFIG_H

#include <memory>
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <unordered_map>
#include <yaml-cpp/yaml.h>
#include <vector>
#include <list>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <functional>
#include "util.h"
#include "log.h"
#include "thread.h"

namespace sylar {
    /*
     * 配置基类，每一项配置对应一个配置类，包含配置名，配置值和描述
     */
    class ConfigVarBase {
    public:
        using ptr = std::shared_ptr<ConfigVarBase>;

        /*
         * @param[in] name 配置参数名称[0-9a-z_.]
         * @param[in] description 配置参数描述
         * 将字母转为小写
         */
        ConfigVarBase(const std::string &name, const std::string &description)
                : m_name(name), m_description(description) {
            std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);
        }

        virtual ~ConfigVarBase() {}

        const std::string &getName() const { return m_name; }

        const std::string &getDescription() const { return m_description; }

        /*
         * 配置value转成字符串
         */
        virtual std::string toString() = 0;

        /*
         * 从string初始化值
         */
        virtual bool fromString(const std::string &val) = 0;

        virtual std::string getTypeName() const = 0;

    protected:
        std::string m_name;
        std::string m_description;
    };

    /*
     * 类型转换模板类(F 源类型, T 目标类型)
     */
    template<class F, class T>
    class LexicalCast {
    public:
        T operator()(const F &v) {
            return boost::lexical_cast<T>(v);
        }
    };

    template<class T>
    class LexicalCast<std::string, std::vector<T>> {
    public:
        std::vector<T> operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            std::vector<T> vec;
            std::stringstream ss;
            for (size_t i = 0; i < node.size(); ++i) {
                ss.str("");
                ss << node[i];
                vec.push_back(LexicalCast<std::string, T>()(ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::vector<T>, std::string> {
    public:
        std::string operator()(const std::vector<T> &vec) {
            YAML::Node node;
            for (auto &i:vec) {
                node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string, std::list<T>> {
    public:
        std::list<T> operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            std::list<T> vec;
            std::stringstream ss;
            for (size_t i = 0; i < node.size(); ++i) {
                ss.str("");
                ss << node[i];
                vec.push_back(LexicalCast<std::string, T>()(ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::list<T>, std::string> {
    public:
        std::string operator()(const std::list<T> &vec) {
            YAML::Node node;
            for (auto &i:vec) {
                node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string, std::set<T>> {
    public:
        std::set<T> operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            std::set<T> vec;
            std::stringstream ss;
            for (size_t i = 0; i < node.size(); ++i) {
                ss.str("");
                ss << node[i];
                vec.insert(LexicalCast<std::string, T>()(ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::set<T>, std::string> {
    public:
        std::string operator()(const std::set<T> &vec) {
            YAML::Node node;
            for (auto &i:vec) {
                node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string, std::unordered_set<T>> {
    public:
        std::unordered_set<T> operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            std::unordered_set<T> vec;
            std::stringstream ss;
            for (size_t i = 0; i < node.size(); ++i) {
                ss.str("");
                ss << node[i];
                vec.insert(LexicalCast<std::string, T>()(ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::unordered_set<T>, std::string> {
    public:
        std::string operator()(const std::unordered_set<T> &vec) {
            YAML::Node node;
            for (auto &i:vec) {
                node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string, std::map<std::string, T>> {
    public:
        std::map<std::string, T> operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            std::map<std::string, T> vec;
            std::stringstream ss;
            for (auto it = node.begin(); it != node.end(); it++) {
                ss.str("");
                ss << it->second;
                vec.insert(std::make_pair(it->first.Scalar(), LexicalCast<std::string, T>()(ss.str())));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::map<std::string, T>, std::string> {
    public:
        std::string operator()(const std::map<std::string, T> &vec) {
            YAML::Node node;
            for (auto &i:vec) {
                node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string, std::unordered_map<std::string, T>> {
    public:
        std::unordered_map<std::string, T> operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            std::unordered_map<std::string, T> vec;
            std::stringstream ss;
            for (auto it = node.begin(); it != node.end(); it++) {
                ss.str("");
                ss << it->second;
                vec.insert(std::make_pair(it->first.Scalar(), LexicalCast<std::string, T>()(ss.str())));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::unordered_map<std::string, T>, std::string> {
    public:
        std::string operator()(const std::unordered_map<std::string, T> &vec) {
            YAML::Node node;
            for (auto &i:vec) {
                node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    /*
     * 配置参数模板子类,保存对应类型的参数值
     *          T 参数的具体类型
     *          FromStr 从std::string转换成T类型的仿函数
     *          ToStr 从T转换成std::string的仿函数
     *          std::string 为YAML格式的字符串
     */
    template<class T, class FromStr = LexicalCast<std::string, T>,
            class ToStr = LexicalCast<T, std::string>>
    class ConfigVar : public ConfigVarBase {
    public:
        using ptr = std::shared_ptr<ConfigVar>;
        using RWMutexType = RWMutex;
        // 配置更改的时候调用回调函数
        using on_change_call_back = std::function<void(const T &old_value, const T &new_value)>;

        ConfigVar(const std::string &name, const T &default_value, const std::string &description = "")
                : ConfigVarBase(name, description), m_val(default_value) {}

        /*
         * 将参数值转换成YAML String
         */
        std::string toString() override {
            try {
//                return boost::lexical_cast<std::string>(m_val);
                RWMutexType::ReadLock lock(m_mutex);
                return ToStr()(m_val);
            } catch (std::exception &e) {
                SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception "
                                                  << e.what() << " convert: " << typeid(m_val).name() << " to string";
            }
            return "";
        }

        /*
         * 从YAML String 转成参数的值
         */
        bool fromString(const std::string &val) override {
            try {
//                m_val = boost::lexical_cast<T>(val);
                setVal(FromStr()(val));
            } catch (std::exception &e) {
                SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString exception "
                                                  << e.what() << " convert: string to " << typeid(m_val).name();
            }
            return false;
        }

        const T getVal() {
            RWMutexType::ReadLock lock(m_mutex);
            return m_val;
        }

        void setVal(const T &val) {
            {
                RWMutexType::ReadLock lock(m_mutex);
                if (val == m_val) {
                    return;
                }
                for (auto &i:m_cbs) {
                    i.second(m_val, val);
                }
            }
            RWMutexType::WriteLock lock(m_mutex);
            m_val = val;
        }

        std::string getTypeName() const override {
            return TypeToName<T>();
        }

        uint64_t addListener(on_change_call_back cb) {
            static uint64_t s_fun_id = 0;
            RWMutexType::WriteLock lock(m_mutex);
            ++s_fun_id;
            m_cbs[s_fun_id] = cb;
            return s_fun_id;
        }

        void delListener(uint64_t key) {
            RWMutexType::WriteLock lock(m_mutex);
            m_cbs.erase(key);
        }

        void clearListener() {
            RWMutexType::WriteLock lock(m_mutex);
            m_cbs.clear();
        }

        on_change_call_back getListener(uint64_t key) {
            RWMutexType::ReadLock lock(m_mutex);
            auto it = m_cbs.find(key);
            return it == m_cbs.end() ? nullptr : it->second;
        }

    private:
        RWMutexType m_mutex;
        T m_val;
        //变更回调函数组，要求uint64_t key唯一，一般可以用hash
        std::map<uint64_t, on_change_call_back> m_cbs;
    };

    /*
     * ConfigVar的管理类
     */
    class Config {
    public:
        using ConfigVarMap = std::unordered_map<std::string, ConfigVarBase::ptr>;
        using RWMutexType = RWMutex;

        /*
         * 获取/创建对应参数名的配置参数
         */
        template<class T>
        static typename ConfigVar<T>::ptr
        Lookup(const std::string &name, const T &default_value, const std::string &description = "") {
            RWMutexType::WriteLock lock(GetMutex());
            auto it = GetDatas().find(name);
            if (it != GetDatas().end()) {
                auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
                if (tmp) {
                    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exits";
                    return tmp;
                } else {
                    SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exits but the type is not "
                                                      << TypeToName<T>() << ", real_type=" << it->second->getTypeName()
                                                      << " " << it->second->toString();
                    return nullptr;
                }
            }
            if (name.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678") != std::string::npos) {
                SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name invalid " << name;
                throw std::invalid_argument(name);
            }
            typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description));
            GetDatas()[name] = v;
            return v;
        }

        /*
         * 查询m_data map中的name配置
         */
        template<class T>
        static typename ConfigVar<T>::ptr Lookup(const std::string &name) {
            RWMutexType::ReadLock lock(GetMutex());
            auto it = GetDatas().find(name);
            if (it == GetDatas().end()) {
                return nullptr;
            }
            return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
        }

        /*
         * 使用YAML::Node初始化配置模块
         */
        static void LoadFromYaml(const YAML::Node &root);

        /*
         * 查找配置参数,返回配置参数的基类
         */
        static ConfigVarBase::ptr LookupBase(const std::string &name);

        static void Visit(std::function<void(ConfigVarBase::ptr)> cb);

    private:
        static ConfigVarMap &GetDatas() {
            static ConfigVarMap m_datas;
            return m_datas;
        }

        static RWMutexType &GetMutex() {
            static RWMutexType s_mutex;
            return s_mutex;
        }
    };
}
#endif //SYLAR_CONFIG_H
